//
//  CollectionAction.swift
//  Risac
//
//  Created by remy on 2018/8/8.
//

import UIKit

/// UICollectionView 的 delegate 代理对象
public final class CollectionAction: NSObject {
    
    public enum ActionType {
        case tap, display, end
    }
    
    public typealias ActionClosure = (CollectionCellItem, IndexPath) -> Void
    public var isCellHighlight = false
    public var isCellDeselect = true
    public var forwardDelegates = NSHashTable<UIScrollViewDelegate>.weakObjects()
    public var cellActions: [AnyHashable: [AnyHashable: ActionClosure]] = [:]
    public var cellsActions: [AnyHashable: [AnyHashable: ActionClosure]] = [:]
    
    public func handle<T: CollectionCellItem>(_ type: ActionType,
                                              cellItem: T,
                                              action: @escaping (T, IndexPath) -> Void) {
        var dict = cellActions[type] ?? [:]
        let closure: ActionClosure = {
            action($0 as! T, $1)
        }
        dict[cellItem.hashValue] = closure
        cellActions[type] = dict
    }
    
    public func clearHandle<T: CollectionCellItem>(_ type: ActionType,
                                                   cellItem: T? = nil) {
        if let cellItem = cellItem {
            cellActions[type]?.removeValue(forKey: cellItem.hashValue)
        } else {
            cellActions[type] = [:]
        }
    }
    
    public func handle<T: CollectionCellItem>(_ type: ActionType,
                                              cellItemClass: T.Type,
                                              action: @escaping (T, IndexPath) -> Void) {
        var dict = cellsActions[type] ?? [:]
        let closure: ActionClosure = {
            action($0 as! T, $1)
        }
        dict[cellItemClass.hash()] = closure
        cellsActions[type] = dict
    }
    
    public func clearHandle<T: CollectionCellItem>(_ type: ActionType,
                                                   cellItemClass: T.Type? = nil) {
        if let cellItemClass = cellItemClass {
            cellsActions[type]?.removeValue(forKey: cellItemClass.hash())
        } else {
            cellsActions[type] = [:]
        }
    }
}

extension CollectionAction {
    
    public override func responds(to aSelector: Selector!) -> Bool {
        if super.responds(to: aSelector) {
            return true
        } else if shouldForwardSelector(aSelector: aSelector) {
            // 接收UICollectionViewDelegate中未实现的代理方法,如果forwardDelegates中有对象实现该方法则返回true,触发消息转发流程
            for delegate in forwardDelegates.allObjects {
                if delegate.responds(to: aSelector) {
                    return true
                }
            }
        }
        return false
    }
    
    // 消息转发流程
    public override func forwardingTarget(for aSelector: Selector!) -> Any? {
        for delegate in forwardDelegates.allObjects {
            if delegate.responds(to: aSelector) {
                return delegate
            }
        }
        return super.forwardingTarget(for: aSelector)
    }
}

extension CollectionAction {
    
    private func shouldForwardSelector(aSelector: Selector) -> Bool {
        let description = protocol_getMethodDescription(UICollectionViewDelegateFlowLayout.self, aSelector, false, true)
        return description.name != nil && description.types != nil
    }
    
    private func getAction(_ type: ActionType, _ cellItem: CollectionCellItem) -> ActionClosure? {
        if let action = cellActions[type]?[cellItem.hashValue] {
            return action
        } else if let action = cellsActions[type]?[Swift.type(of: cellItem).hash()] {
            return action
        }
        return nil
    }
}

extension CollectionAction: UICollectionViewDelegate {
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if let item = (collectionView.dataSource as? CollectionModel)?.cellItem(at: indexPath) {
            if let action = getAction(.display, item) {
                action(item, indexPath)
            }
        }
    }
    
    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if let item = (collectionView.dataSource as? CollectionModel)?.cellItem(at: indexPath) {
            if let action = getAction(.end, item) {
                action(item, indexPath)
            }
        }
    }
    
    // 不提供-collectionView:shouldHighlightItemAtIndexPath:,返回false会导致didSelectItemAt不触发
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if let item = (collectionView.dataSource as? CollectionModel)?.cellItem(at: indexPath), let action = getAction(.tap, item) {
            action(item, indexPath)
            if isCellDeselect { collectionView.deselectItem(at: indexPath, animated: true) }
        }
    }
}

extension CollectionAction: UICollectionViewDelegateFlowLayout {
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if let size = (collectionView.dataSource as? CollectionModel)?.cellItem(at: indexPath)?.cellSize {
            return size
        }
        return .zero
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        if let size = (collectionView.dataSource as? CollectionModel)?.sectionItems[safe: section]?.sectionInset {
            return size
        }
        return .zero
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        if let value = (collectionView.dataSource as? CollectionModel)?.sectionItems[safe: section]?.lineSpacing {
            return value
        }
        return 0.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        if let value = (collectionView.dataSource as? CollectionModel)?.sectionItems[safe: section]?.itemSpacing {
            return value
        }
        return 0.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        if let height = (collectionView.dataSource as? CollectionModel)?.sectionItems[safe: section]?.sectionHeaderHeight {
            return CGSize(width: 0.0, height: height)
        }
        return .zero
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        if let height = (collectionView.dataSource as? CollectionModel)?.sectionItems[safe: section]?.sectionFooterHeight {
            return CGSize(width: 0.0, height: height)
        }
        return .zero
    }
}
